/*
 * Copyright 2009, 2013, Zhou Zhenghui <zhouzhenghui@gmail.com>
 */

#include "continuation/continuation_pthread.h"

pthread_key_t __async_pthread_key;

static void * __async_pthread_run(struct __AsyncTask * async_task);
static struct __AsyncTask *async_copy_stack_frame(struct __AsyncTask *async_task);

/* these function pointers prevent link-time optimization */
struct __AsyncTask *(*__async_copy_stack_frame)(struct __AsyncTask *) = &async_copy_stack_frame;

static void make_key()
{
  pthread_key_create(&__async_pthread_key, NULL);
}

pthread_t __async_pthread_create()
{
  static pthread_once_t __async_pthread_once = PTHREAD_ONCE_INIT;
  pthread_t pthread_id;
  int error;
  struct __AsyncTask *async_task = (struct __AsyncTask *)malloc(sizeof(struct __AsyncTask));
  pthread_once(&__async_pthread_once, make_key);
  async_task->quitable = 0;
  pthread_mutex_init(&async_task->mutex, NULL);
  pthread_cond_init(&async_task->running, NULL);
  /* run thread with async_task as it's argument */
  pthread_mutex_lock(&async_task->mutex);
  error = pthread_create(&pthread_id, NULL, (void *(*)(void *))&__async_pthread_run, (void *)async_task);
  if (error) {
      free(async_task);
      async_task = NULL;
  }
  pthread_setspecific(__async_pthread_key, async_task);
  return pthread_id;
}

static void * __async_pthread_run(struct __AsyncTask * async_task)
{
  /* ensure the parent thread had prepared the continuation */
  pthread_mutex_lock(&async_task->mutex);
  pthread_mutex_unlock(&async_task->mutex);
  assert(async_task->cont_stub.cont == &async_task->cont);
  continuation_stub_invoke(&async_task->cont_stub);
  /* ensure the parent thread had released the lock */
  pthread_mutex_lock(&async_task->mutex);
  if (!async_task->quitable) {
    pthread_cond_wait(&async_task->running, &async_task->mutex);
  }
  pthread_mutex_unlock(&async_task->mutex);
  pthread_cond_destroy(&async_task->running);
  pthread_mutex_destroy(&async_task->mutex);
  free(async_task);
  return NULL;
}

static struct __AsyncTask *async_copy_stack_frame(struct __AsyncTask *async_task)
{
  struct __ContinuationStub *cont_stub = &async_task->cont_stub;
  struct __Continuation *cont = &async_task->cont;
  memcpy(cont_stub->addr.stack_frame_tail
          , cont->stack_frame_tail
          , cont->stack_frame_size);
  return async_task;
}
